package br.edu.ufc.deti.broker.mapeamento.castor.utils;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;

import org.castor.mapping.BindingType;
import org.castor.mapping.MappingUnmarshaller;
import org.castor.xml.BackwardCompatibilityContext;
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.mapping.MappingLoader;
import org.exolab.castor.xml.ClassDescriptorResolver;
import org.exolab.castor.xml.ClassDescriptorResolverFactory;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.Unmarshaller;
import org.exolab.castor.xml.ValidationException;
import org.exolab.castor.xml.XMLClassDescriptorResolver;
import org.exolab.castor.xml.XMLContext;
import org.xml.sax.InputSource;

/**
 * Classe utilit�ria que conter� os m�todos que transformam objeto java em xml e vice-versa.
 * 
 * @since January 11, 2008.
 * @author Marcelo Henrique.
 */
public class CastorUtil {
	
	/**
	 * Vari�vel que conter� o arquivo xml a ser gerado a partir das classes Java.
	 */
	private StringWriter writer = new StringWriter();
	
	/**
	 * Vari�vel que cont�m os mapeamentos a serem utilizados.
	 */
	private Mapping mapping;
	
	/**
	 * Vari�vel repons�vel pela convers�o de objeto Java para XML.
	 */
	private Marshaller marshaller;
	
	/**
	 * Vari�vel respons�vel pela convers�o de XML para Java.
	 */
	private Unmarshaller unmarshaller;
	
	/**
	 * Vari�vel de contexto que possibilita a cria��o de inst�ncias de
	 * Marshaller e Unmarshaller de forma mais perform�tica.
	 */
	private static XMLContext context = new XMLContext();
	
	/**
	 * M�todo construtor default onde s�o inicializados algumas vari�veis
	 * utilizadas pelos m�todos dessa classe.
	 * @throws IOException.
	 */
	public CastorUtil() throws IOException {
		init();
	}
	
	/**
	 * M�todo construtor que recebe o arquivo de mapeamento.
	 * @param mapeamento Caminho relativo ao caminho de mapeamento.
	 * @throws IOException.
	 * @throws MappingException.
	 */
	public CastorUtil( final String mapeamento ) throws IOException, MappingException {
		init();
		loadMapping( mapeamento );
	}
	
	/**
	 * M�todo construtor que recebe caminhos relativos ao arquivos
	 * de mapeamento.
	 * @param mapeamentos Caminhos relativos aos arquivos de mapeamento.
	 * @throws IOException.
	 * @throws MappingException.
	 */
	public CastorUtil( final String[] mapeamentos ) throws IOException, MappingException {
		init();
		loadMapping( mapeamentos );
	}
	
	/**
	 * M�todo construtor que recebe o pr�prio mapeamento
	 * encapsulado dentro de um InputSource.
	 * @param mapeamento Mapeamento.
	 * @throws IOException.
	 * @throws MappingException.
	 */
	public CastorUtil( final InputSource mapeamento ) throws IOException, MappingException {
		init();
		loadMapping( mapeamento );
	}
	
	/**
	 * M�todo para inicializar algumas propriedades.
	 * @throws IOException.
	 */
	private void init() throws IOException {
		// Configurações para o objeto Marshaller.
		marshaller = context.createMarshaller();
		marshaller.setWriter( writer );
		marshaller.setEncoding( "ISO-8859-1" );
		// Configurações para o objeto Unmarshaller.
		unmarshaller = context.createUnmarshaller();
		unmarshaller.setIgnoreExtraAttributes( true );
		unmarshaller.setIgnoreExtraElements( true );
	}
	
	/**
	 * M�todo que serve para carregar um �nico arquivo de mapeamento e retornar
	 * um objeto Mapping com o mapeamento carregado em mem�ria.
	 * @param mapeamento Caminho relativo at� o arquivo de mapeamento para ser carregado.
	 * @return Uma refer�ncia ao pr�prio objeto.
	 * @throws IOException.
	 * @throws MappingException.
	 */
	public CastorUtil loadMapping( final String mapeamento ) throws IOException, MappingException {
		if( mapping == null )
			mapping = context.createMapping();
		mapping.loadMapping( getClass().getResource( mapeamento ) );
		return this;
	}
	
	/**
	 * Esse m�todo serve para carregar arquivos de mapeamento que est�o no classloader assim
	 * � passado o caminho relativo do recurso que aqui no caso � um arquivo de mapeamento do
	 * castor e carrega ele na mem�ria.
	 * @param mapeamentos[] Um array com os caminhos relativos.
	 * @return Uma refer�ncia ao pr�prio objeto.
	 * @throws IOException.
	 * @throws MappingException.
	 */
	public CastorUtil loadMapping( final String mapeamentos[] ) throws IOException, MappingException {
		for( int i = 0; i < mapeamentos.length; i++ )
			loadMapping( mapeamentos[ i ] );
		return this;
	}
	
	/**
	 * M�todo que serve para carregar um mapeamento que est� na memoria por meio de um objeto
	 * do tipo InputSource.
	 * @param mapeamento Objeto do tipo InputSource que cont�m o mapeamento do castor.
	 * @return Uma refer�ncia ao pr�prio objeto.
	 * @throws IOException.
	 * @throws MappingException.
	 */
	public CastorUtil loadMapping( final InputSource mapeamento ) throws IOException, MappingException {
		if( mapping == null )
			mapping = context.createMapping();
		mapping.loadMapping( mapeamento );
		return this;
	}
	
	/**
	 * M�todo est�tico que retorna um novo objeto do tipo Mapping de mapeamento Java/XML.
	 * @param mapeamento Caminho relativo at� o arquivo de mapeamento para ser carregado.
	 * @return Objeto de mapeamento do castor.
	 * @throws IOException.
	 * @throws MappingException.
	 */
	private static Mapping getMapping( final String mapeamento ) throws IOException, MappingException {
		Mapping mapping = context.createMapping();
		mapping.loadMapping( CastorUtil.class.getResource( mapeamento ) );
		return mapping;
	}
	
	/**
	 * M�todo est�tico que retorna um novo objeto do tipo Mapping de mapeamento Java/XML.
	 * @param mapeamento Array com caminhos relativos at� o arquivo de mapeamento para ser carregado.
	 * @return Objeto de mapeamento do castor.
	 * @throws IOException.
	 * @throws MappingException.
	 */
	private static Mapping getMapping( final String[] mapeamento ) throws IOException, MappingException {
		Mapping mapping = context.createMapping();
		for( int i = 0; i < mapeamento.length; i++ )
			mapping.loadMapping( CastorUtil.class.getResource( mapeamento[ i ] ) );
		return mapping;
	}
	
	/**
	 * M�todo est�tico que retorna um novo objeto do tipo Mapping de mapeamento Java/XML.
	 * @param mapeamento Objeto do tipo InputSource que cont�m as informa��es sobre o mapeamento Java/XML.
	 * @return Objeto de mapeamento do castor.
	 * @throws IOException.
	 * @throws MappingException.
	 */
	private static Mapping getMapping( final InputSource mapeamento ) throws IOException, MappingException {
		Mapping mapping = context.createMapping();
		mapping.loadMapping( mapeamento );
		return mapping;
	}
	
	/**
	 * M�todo criado para fazer marshal de objetos sem um
	 * arquivo de mapeamento. Caso o objeto interno de mapeamento esteja setado
	 * ent�o esse mapeamento ser� utilizado.
	 * @param object O objeto a ser convertido em xml.
	 * @return Uma string cujo conte�do � um xml e caso o m�todo
	 * n�o seja bem sucedido o valor null.
	 * @throws IOException.
	 * @throws MappingException.
	 * @throws MarshalException.
	 * @throws ValidationException.
	 */
	public String marshal( Object object ) throws IOException, MarshalException, ValidationException, MappingException {
		if( mapping != null )
			marshaller.setMapping( mapping );
		marshaller.marshal( object );
		return getOutPut();
	}
	
	/**
	 * M�todo mais gen�rico que serve para fazer a opera��o marshal, ou seja,
	 * serve para converter um objeto java em um arquivo xml.
	 * @param mapping Arquivo de mapeamento do castor.
	 * @param object Objeto que ter� suas informa��es extraidas para o xml.
	 * @return Uma string no formato de xml com as informa��es do objeto java.
	 * @throws IOException.
	 * @throws MappingException.
	 * @throws MarshalException.
	 * @throws ValidationException.
	 */
	public String marshal( String mapping, Object object ) throws IOException, MappingException, MarshalException, ValidationException {
		// Carregando o mapeamento.
		loadMapping( mapping );
		marshaller.setMapping( this.mapping );
		marshaller.marshal( object );
		return getOutPut();
	}
	
	/**
	 * M�todo mais gen�rico que serve para fazer a opera��o marshal, ou seja,
	 * serve para converter um objeto java em um arquivo xml.
	 * @param mapping[] Arquivos de mapeamento do castor.
	 * @param object Objeto que ter� suas informa��es extraidas para o xml.
	 * @return Uma string no formato de xml com as informa��es do objeto java.
	 * @throws IOException.
	 * @throws MappingException.
	 * @throws MarshalException.
	 * @throws ValidationException.
	 */
	public String marshal( String mapping[], Object object ) throws IOException, MappingException, MarshalException, ValidationException {
		// Carregando o mapeamento.
		loadMapping( mapping );
		marshaller.setMapping( this.mapping );
		marshaller.marshal( object );
		return getOutPut();
	}
	
	/**
	 * M�todo mais gen�rico que serve para fazer a opera��o marshal, ou seja,
	 * serve para converter um objeto java em um arquivo xml.
	 * @param mapping Objeto do tipo InputSource que cont�m o mapeamento do castor.
	 * @param object Objeto que ter� suas informa��es extraidas para o xml.
	 * @return Uma string no formato de xml com as informa��es do objeto java.
	 * @throws IOException.
	 * @throws MappingException.
	 * @throws MarshalException.
	 * @throws ValidationException.
	 */
	public String marshal( InputSource mapping, Object object ) throws IOException, MappingException, MarshalException, ValidationException {
		// Carregando o mapeamento.
		loadMapping( mapping );
		marshaller.setMapping( this.mapping );
		marshaller.marshal( object );
		return getOutPut();
	}
	
	/**
	 * M�todo que converte um objeto Java para uma string no formato XML recebendo como argumento
	 * um objeto do tipo ClassDescriptorResolver que consegue obter mais desempenho em rela��o aos
	 * outros m�todos porque o custo de carregamento do mapeamento � muito grande e atrav�s desse
	 * objeto esse pre�o n�o � pago.
	 * @param cdResolver Objeto do tipo ClassDescriptorResolver que cont�m as informa��es do mapeamento.
	 * @param object Objeto que ter� suas informa��es extraidas para o xml.
	 * @return Uma string no formato de xml com as informa��es do objeto java.
	 * @throws MarshalException.
	 * @throws ValidationException.
	 */
	public String marshal( ClassDescriptorResolver cdResolver, Object object ) throws MarshalException, ValidationException {
		marshaller.setResolver( ( XMLClassDescriptorResolver ) cdResolver );
		marshaller.marshal( object );
		return getOutPut();
	}
	
	/**
	 * M�todo mais gen�rico que serve para fazer a opera��o de unmarshal, ou seja,
	 * serve para converter um arquivo xml em um determinado objeto que � passado
	 * como argumento para o m�todo. Nesse m�todo n�o � utilizado um arquivo de mapeamento.
	 * @param object Objeto que ser� populado com o conte�do do xml.
	 * @param xml O xml que ter� as informa��es extra�das para o objeto java.
	 * @return O objeto java populado.
	 * @throws MappingException.
	 * @throws MarshalException.
	 * @throws ValidationException.
	 */
	public Object unmarshal( Object object, String xml ) throws MarshalException, ValidationException, MappingException {
		if( mapping != null )
			unmarshaller.setMapping( mapping );
		unmarshaller.setObject( object );
		object = unmarshaller.unmarshal( new StringReader( xml ) );
		return object;
	}
	
	/**
	 * M�todo mais gen�rico que serve para fazer a opera��o de unmarshal, ou seja,
	 * serve para converter um arquivo xml em um determinado objeto que � passado
	 * como argumento para o m�todo.
	 * @param mapping Arquivo de mapeamento com o caminho relativo at� ele.
	 * @param object Objeto que ser� populado com o conte�do do xml.
	 * @param xml O xml que ter� as informa��es extra�das para o objeto java.
	 * @return O objeto java populado.
	 * @throws IOException.
	 * @throws MappingException.
	 * @throws MarshalException.
	 * @throws ValidationException.
	 */
	public Object unmarshal( String mapping, Object object, String xml ) throws MappingException, IOException, MarshalException, ValidationException {
		// Carregando o mapeamento.
		loadMapping( mapping );
		unmarshaller.setMapping( this.mapping );
		unmarshaller.setObject( object );
		object = unmarshaller.unmarshal( new StringReader( xml ) );
		return object;
	}
	
	/**
	 * M�todo mais gen�rico que serve para fazer a opera��o de unmarshal, ou seja,
	 * serve para converter um arquivo xml em um determinado objeto que � passado
	 * como argumento para o m�todo.
	 * @param mapping[] Arquivos de mapeamento com o caminho relativo at� ele.
	 * @param object Objeto que ser� populado com o conte�do do xml.
	 * @param xml O xml que ter� as informa��es extra�das para o objeto java.
	 * @return O objeto java populado.
	 * @throws IOException.
	 * @throws MappingException.
	 * @throws MarshalException.
	 * @throws ValidationException.
	 */
	public Object unmarshal( String mapping[], Object object, String xml ) throws MarshalException, ValidationException, MappingException, IOException {
		// Carregando o mapeamento.
		loadMapping( mapping );
		unmarshaller.setMapping( this.mapping );
		unmarshaller.setObject( object );
		object = unmarshaller.unmarshal( new StringReader( xml ) );
		return object;
	}
	
	/**
	 * M�todo mais gen�rico que serve para fazer a opera��o de unmarshal, ou seja,
	 * serve para converter um arquivo xml em um determinado objeto que � passado
	 * como argumento para o m�todo.
	 * @param mapping Objeto do tipo InputSource que cont�m o mapeamento do castor.
	 * @param object Objeto que ser� populado com o conte�do do xml.
	 * @param xml O xml que ter� as informa��es extra�das para o objeto java.
	 * @return O objeto java populado.
	 * @throws IOException.
	 * @throws MappingException.
	 * @throws MarshalException.
	 * @throws ValidationException.
	 */
	public Object unmarshal( InputSource mapping, Object object, String xml ) throws MarshalException, ValidationException, MappingException, IOException {
		// Carregando o mapeamento.
		loadMapping( mapping );
		unmarshaller.setMapping( this.mapping );
		unmarshaller.setObject( object );
		object = unmarshaller.unmarshal( new StringReader( xml ) );
		return object;
	}
	
	/**
	 * M�todo mais gen�rico que serve para fazer a opera��o de unmarshal, ou seja,
	 * serve para converter um arquivo xml em um determinado objeto que � passado
	 * como argumento para o m�todo.
	 * @param cdResolver Objeto do tipo ClassDescriptorResolver que cont�m as informa��es do mapeamento.
	 * @param object Objeto que ser� populado com o conte�do do xml.
	 * @param xml O xml que ter� as informa��es extra�das para o objeto java.
	 * @return O objeto java populado.
	 * @throws MarshalException.
	 * @throws ValidationException.
	 */
	public Object unmarshal( ClassDescriptorResolver cdResolver, Object object, String xml ) throws MarshalException, ValidationException {
		unmarshaller.setResolver( ( XMLClassDescriptorResolver ) cdResolver );
		unmarshaller.setObject( object );
		object = unmarshaller.unmarshal( new StringReader( xml ) );
		return object;
	}
	
	/**
	 * M�todo que gera um objeto do tipo ClassDescriptorResolver que � uma inst�ncia do mapeamento a ser utilizado
	 * pelo Castor para realizar as opera��es de marshal e unmarshal. O objetivo desse m�todo � que as classe que
	 * se utilizam dos m�todo da classe CastorUtil gere um objeto do tipo ClassDescriptorResolver para n�o ter overhead
	 * com rela��o ao carregamento do mapeamento para as classe Marshaller e Unmarshaller.
	 * @param mapeamento Caminho relativo at� o arquivo contendo as configura��es de mapeamento.
	 * @return Objeto do tipo ClassDescriptorResolver que cont�m as informa��es sobre o mapeamento.
	 * @throws IOException.
	 * @throws MappingException.
	 */
	public static ClassDescriptorResolver getClassDescriptorResolver( final String mapeamento ) throws IOException, MappingException {
		Mapping mapping = getMapping( mapeamento );
		XMLClassDescriptorResolver classDescriptorResolver = ( XMLClassDescriptorResolver ) ClassDescriptorResolverFactory.createClassDescriptorResolver( BindingType.XML );
		classDescriptorResolver.setInternalContext( new BackwardCompatibilityContext() );
		MappingUnmarshaller mappingUnmarshaller = new MappingUnmarshaller();
		MappingLoader mappingLoader = mappingUnmarshaller.getMappingLoader( mapping, BindingType.XML );
		classDescriptorResolver.setMappingLoader( mappingLoader );
		return classDescriptorResolver;
	}
	
	/**
	 * M�todo que gera um objeto do tipo ClassDescriptorResolver que � uma inst�ncia do mapeamento a ser utilizado
	 * pelo Castor para realizar as opera��es de marshal e unmarshal. O objetivo desse m�todo � que as classe que
	 * se utilizam dos m�todo da classe CastorUtil gere um objeto do tipo ClassDescriptorResolver para n�o ter overhead
	 * com rela��o ao carregamento do mapeamento para as classe Marshaller e Unmarshaller.
	 * @param mapeamento Array de caminhos relativos at� o arquivo contendo as configura��es de mapeamento.
	 * @return Objeto do tipo ClassDescriptorResolver que cont�m as informa��es sobre o mapeamento.
	 * @throws IOException.
	 * @throws MappingException.
	 */
	public static ClassDescriptorResolver getClassDescriptorResolver( final String[] mapeamento ) throws IOException, MappingException {
		Mapping mapping = getMapping( mapeamento );
		XMLClassDescriptorResolver classDescriptorResolver = ( XMLClassDescriptorResolver ) ClassDescriptorResolverFactory.createClassDescriptorResolver( BindingType.XML );
		classDescriptorResolver.setInternalContext( new BackwardCompatibilityContext() );
		MappingUnmarshaller mappingUnmarshaller = new MappingUnmarshaller();
		MappingLoader mappingLoader = mappingUnmarshaller.getMappingLoader( mapping, BindingType.XML );
		classDescriptorResolver.setMappingLoader( mappingLoader );
		return classDescriptorResolver;
	}
	
	/**
	 * M�todo que gera um objeto do tipo ClassDescriptorResolver que � uma inst�ncia do mapeamento a ser utilizado
	 * pelo Castor para realizar as opera��es de marshal e unmarshal. O objetivo desse m�todo � que as classe que
	 * se utilizam dos m�todo da classe CastorUtil gere um objeto do tipo ClassDescriptorResolver para n�o ter overhead
	 * com rela��o ao carregamento do mapeamento para as classe Marshaller e Unmarshaller.
	 * @param mapeamento Objeto do tipo InputSource que cont�m as informa��es sobre o mapeamento Java/XML.
	 * @return Objeto do tipo ClassDescriptorResolver que cont�m as informa��es sobre o mapeamento.
	 * @throws IOException.
	 * @throws MappingException.
	 */
	public static ClassDescriptorResolver getClassDescriptorResolver( final InputSource mapeamento ) throws IOException, MappingException {
		Mapping mapping = getMapping( mapeamento );
		ClassDescriptorResolver classDescriptorResolver = ClassDescriptorResolverFactory.createClassDescriptorResolver( BindingType.XML );
		MappingUnmarshaller mappingUnmarshaller = new MappingUnmarshaller();
		MappingLoader mappingLoader = mappingUnmarshaller.getMappingLoader( mapping, BindingType.XML );
		classDescriptorResolver.setMappingLoader( mappingLoader );
		return classDescriptorResolver;
	}
	
	/**
	 * M�todo para ajustar a sa�da da String XML ap�s a convers�o.
	 * @return String no formato XML com as informa��es do objeto Java.
	 */
	private String getOutPut() {
		return writer.toString().replace( '\n', ' ' );
	}
	
	/**
	 * M�todo utilizado para limpar o objeto de mapeamento caso
	 * n�o seja necess�rio utiliz�-lo.
	 * @return Uma refer�ncia ao pr�prio objeto.
	 */
	public CastorUtil clearMapping() {
		mapping = null;
		return this;
	}
	
}
